
#include "common.h"
#include "conf.h"
#include "globals.h"
#include "cache_index.h"
#include "cache_dir.h"
#include "cache_file.h"
#include "http.h"


static long long cache_num;

static void cache_index_read(cache_dir_t *dir);
static void cache_index_log(int index_fd, void *data, int size);


/* -1 : failed; 0 : success */
int cache_index_add(cache_file_t *cache, int op)
{
	assert(conf);
	assert(conf->cache.dir);

	if(!cache) {
		debug_log(5, "cache is %p, return -1\n", cache);
		return -1;
	}

	cache_index_t ci;
	cache_dir_t *d = conf->cache.dir->items[cache->dir_no];
	
	/* build cache index */
	memcpy(ci.key, cache->key, 16);
	ci.time = cache->time;
	ci.header_size = cache->header_size;
	ci.body_size = cache->body_size;
	ci.operate = op;
	
	cache_index_log(d->index_fd, &ci, sizeof(ci));
	return 0;
}


static void cache_index_log(int index_fd, void *data, int size)
{
	if(index_fd == -1) {
		debug_log(1, "cache index log fd %d failed\n",  index_fd);
		return;
	}

	int res = -1;

	res = write(index_fd, data, size);
	if(-1 == res) {
		error_log("write %p, bytes %d failed\n", data, size);
		return;
	}
}

void cache_index_init(void)
{
	assert(conf->cache.dir);
	
	if(conf->cache.dir->count == 0) {
		error_log("%d You do not configure cache dir\n", conf->cache.dir->count);
		abort();
		return;
	}

	int i;
	char path[MAX_BUFFER];
	char new_path[MAX_BUFFER];
	for(i = 0; i < conf->cache.dir->count; i++) {
		debug_log(5, "begin scaning dir %d\n", i);
		cache_dir_t *dentry = conf->cache.dir->items[i];
		cache_index_read(dentry);
		
		/* Need to unlink cache.index ? */
		snprintf(path, MAX_BUFFER - 1, "%s/cache.index", dentry->path);
		snprintf(new_path, MAX_BUFFER - 1, "%s/cache.index.new", dentry->path);
		close(dentry->index_fd);
		rename(new_path, path);
		dentry->index_fd = dentry->new_fd;
		dentry->new_fd = -1;
	}

	debug_log(1, "Finish scan %d files.\n", cache_num);
}

static void cache_index_read(cache_dir_t *dir) 
{
	assert(dir);
	
	int size = sizeof(cache_index_t);
	int res; 
	char buf[MAX_BUFFER];
	int fd = dir->index_fd;

	for(; ;) {
		cache_file_t *cache;
		struct stat sbuf;
		char content[size * 1024];
		int used = 0;

		res = read(fd, content, size * 1024);
		if(-1 == res) {
			error_log("read %d failed\n", fd);
			break;
		}
		else if(0 == res) 
			break;
		while(used < res) {
			cache_index_t ci;
			if(memcpy(&ci, content + used, size)) 
				used += size;
			else {
				fprintf(stderr, "cache_index_read memcpy failed\n");
				abort();
			}
			if(ci.operate == CACHE_INDEX_DEL) {
				cache = cache_file_search_from_key(ci.key);
				if(cache) {
					unlink(cache->file->buf);
					cache_file_delete_and_free(cache);
				}
				continue;
			}

			cache = mempool_alloc(sizeof(*cache));
			assert(cache);
			memcpy(cache->key, ci.key, 16);
			cache->header_size = ci.header_size;
			cache->body_size = ci.body_size;
			cache->dir_no = dir->no;
			cache->time = ci.time;
			cache->human_key = cache_key_ascii(cache->key, DIGEST_LENGTH);
			cache->statu = CACHE_IN_DISK;
			/* get file path */
			res = snprintf(buf, MAX_BUFFER - 1, "%s/c-%s", dir->path, cache->human_key);
			cache->file = buffer_init(buf, res);
			debug_log(5, "cache {%s, h %d, b %d, dir %d}\n", cache->human_key, cache->header_size, \
							cache->body_size, cache->dir_no);
		
			if(-1 == stat(cache->file->buf, &sbuf)) {
				debug_log(1, "%s not exist\n", cache->file->buf);
				cache_file_delete_and_free(cache);
				continue;
			}
			cache_index_log(dir->new_fd, &ci, sizeof(ci));
			if(!cache_file_insert(cache))
				cache_num ++;
		}
	}
	close(fd);
}
